package com.clip.util;

import java.awt.Graphics;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.HeadlessException;
import java.awt.Image;
import java.awt.Transparency;
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.PixelGrabber;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import javax.imageio.ImageIO;
import javax.swing.ImageIcon;

import com.sun.jimi.core.Jimi;

//生成等比例高质量缩略图  
public class ScaleImage {
	private int width;
	private int height;
	private int scaleWidth;
	double support = (double) 3.0;
	double PI = (double) 3.14159265358978;
	double[] contrib;
	double[] normContrib;
	double[] tmpContrib;
	int startContrib, stopContrib;
	int nDots;
	int nHalfDots;

	// public static void main(String[] args) {
	// ScaleImage is = new ScaleImage();
	// try {
	// // is.saveImage("D:/IMG_TEST/img.jpg", "D:/IMG_TEST/test.jpg",
	// // 1440, 900);
	// is.saveImage("D:/IMG_TEST/img_gif.gif", "D:/IMG_TEST/test.gif", 15,15);
	// } catch (Exception e) {
	// // TODO Auto-generated catch block
	// e.printStackTrace();
	// }
	// }
	public String getsuffix(String filename) {
		String cat = filename.substring(filename.lastIndexOf(".") + 1).toUpperCase();
		return cat;
	}
	/**
	 * 图片旋转
	 * 角度为90的倍数
	 */
	public BufferedImage rotateImg(BufferedImage image, int degree) {

		int iw = image.getWidth();// 原始图象的宽度
		int ih = image.getHeight();// 原始图象的高度
		int w = 0;
		int h = 0;
		int x = 0;
		int y = 0;
		degree = degree % 360;
		if (degree < 0)
			degree = 360 + degree;// 将角度转换到0-360度之间
		double ang = degree * 0.0174532925;// 将角度转为弧度

		/**
		 *确定旋转后的图象的高度和宽度
		 */

		if (degree == 180 || degree == 0 || degree == 360) {
			w = iw;
			h = ih;
		} else if (degree == 90 || degree == 270) {
			w = ih;
			h = iw;
		} else {
			int d = iw + ih;
			w = (int) (d * Math.abs(Math.cos(ang)));
			h = (int) (d * Math.abs(Math.sin(ang)));
		}

		x = (w / 2) - (iw / 2);// 确定原点坐标
		y = (h / 2) - (ih / 2);
		BufferedImage rotatedImage = new BufferedImage(w, h, image.getType());
		Graphics gs = rotatedImage.getGraphics();
		gs.fillRect(0, 0, w, h);// 以给定颜色绘制旋转后图片的背景
		AffineTransform at = new AffineTransform();
		at.rotate(ang, w / 2, h / 2);// 旋转图象
		at.translate(x, y);
		AffineTransformOp op = new AffineTransformOp(at,
				AffineTransformOp.TYPE_NEAREST_NEIGHBOR);
		op.filter(image, rotatedImage);
		image = rotatedImage;
		return image;
	}
	/**
	 * 
	 * @param fromFileStr
	 * @param formatWidth
	 * @param formatHeight
	 * @return
	 * @throws Exception
	 */
	public  BufferedImage getNewImage(String fromFileStr,int formatWidth, int formatHeight) throws Exception {
		BufferedImage srcImage = null;
		File fromFile = new File(fromFileStr);
		BufferedInputStream bis = null;
		try{
			bis = new BufferedInputStream(new FileInputStream(fromFile));
			Image image = Jimi.getImage(bis);
			srcImage = toBufferedImage(image);
		}catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
		} finally {
			try {if(bis!=null)bis.close();} catch (IOException e) {e.printStackTrace();}
		}
		if(formatWidth==0||formatHeight==0){
			formatWidth=srcImage.getWidth();
			formatHeight=srcImage.getHeight();
		}
		int img_ori_W = srcImage.getWidth(null);
		int img_ori_H = srcImage.getHeight(null);
		int changeToWideth = 0;
		int changeToHeight = 0;
		if (img_ori_W > 0 && img_ori_H > 0) {
			// flag=true;
			//原始图片和现定图片长宽形状比较是否宽长于高 或者 比例相等 决定是否按宽来缩放高 
			if (img_ori_W / img_ori_H >= formatWidth / formatHeight) {
				if (img_ori_W > formatWidth) {//原始图片宽是否长于现定图片的宽
					changeToWideth = formatWidth;//设置结果图片的宽为限定宽
					changeToHeight = (img_ori_H * formatWidth) / img_ori_W;//(原始图高 * 现定图宽)/原始图宽 = 结果图片的宽
				} else {//原始图片长宽在现定长宽范围以内 
					changeToWideth = img_ori_W;
					changeToHeight = img_ori_H;
				}
			} else {//反之
				if (img_ori_H > formatHeight) {
					changeToHeight = formatHeight;
					changeToWideth = (img_ori_W * formatHeight) / img_ori_H;
				} else {
					changeToWideth = img_ori_W;
					changeToHeight = img_ori_H;
				}
			}
		}
		return srcImage = imageZoomOut(srcImage, changeToWideth, changeToHeight);
		
	}
	
	/**
	 * 根据原图指定大小缩放
	 * 
	 * @param fromFileStr
	 *            原图片地址
	 * @param saveToFileStr
	 *            生成缩略图地址
	 * @param formatWideth
	 *            生成图片宽度
	 * @param formatHeight
	 *            生成图片高度
	 */
	public void saveImage(String fromFileStr, String saveToFileStr,
			int formatWidth, int formatHeight) throws Exception {
		BufferedImage srcImage;
		File saveFile = new File(saveToFileStr);
		if(!saveFile.exists())
			saveFile.mkdirs();
		File fromFile = new File(fromFileStr);
		srcImage = javax.imageio.ImageIO.read(fromFile); // 构建原图对象
		int img_ori_W = srcImage.getWidth(null);
		int img_ori_H = srcImage.getHeight(null);
		int changeToWideth = 0;
		int changeToHeight = 0;
		if (img_ori_W > 0 && img_ori_H > 0) {
			// flag=true;
			// 原始图片和现定图片长宽形状比较是否宽长于高 或者 比例相等 决定是否按宽来缩放高
			if (img_ori_W / img_ori_H >= formatWidth / formatHeight) {
				if (img_ori_W > formatWidth) {// 原始图片宽是否长于现定图片的宽
					changeToWideth = formatWidth;// 设置结果图片的宽为限定宽
					changeToHeight = (img_ori_H * formatWidth) / img_ori_W;// (原始图高
																			// *
																			// 现定图宽)/原始图宽
																			// =
																			// 结果图片的宽
				} else {// 原始图片长宽在现定长宽范围以内
					changeToWideth = img_ori_W;
					changeToHeight = img_ori_H;
				}
			} else {// 反之
				if (img_ori_H > formatHeight) {
					changeToHeight = formatHeight;
					changeToWideth = (img_ori_W * formatHeight) / img_ori_H;
				} else {
					changeToWideth = img_ori_W;
					changeToHeight = img_ori_H;
				}
			}
		}
		srcImage = imageZoomOut(srcImage, changeToWideth, changeToHeight);
		ImageIO.write(srcImage, "JPEG", saveFile);
	}

	
	/**
	 * 传入图片File指定大小缩放
	 * 
	 * @param _fromFile
	 *            原图片文件
	 * @param saveToFileStr
	 *            生成缩略图地址
	 * @param formatWideth
	 *            生成图片宽度
	 * @param formatHeight
	 *            生成图片高度
	 * @return width AND height
	 */
	public Map<String, Integer> saveImage(BufferedImage _img, String savePath,
			double width, double height) throws Exception {
		Map<String, Integer> map = new HashMap<String, Integer>();
		BufferedImage srcImage = _img; // 获得图片对象
		File saveFile = new File(savePath);
		double img_ori_W = srcImage.getWidth(null);
		double img_ori_H = srcImage.getHeight(null);
		int changeToWideth = 0;
		int changeToHeight = 0;
		if (img_ori_W > 0 && img_ori_H > 0) {
			// flag=true;
			// 原始图片和现定图片长宽形状比较是否宽长于高 或者 比例相等 决定是否按宽来缩放高
			if (img_ori_W / img_ori_H >= width / height) {
				if (img_ori_W > width) {// 原始图片宽是否长于现定图片的宽
					changeToWideth = (int) width;// 设置结果图片的宽为限定宽
					changeToHeight = (int) ((img_ori_H * width) / img_ori_W);// (原始图高
																				// *
																				// 现定图宽)/原始图宽
																				// =
																				// 结果图片的宽

				} else {// 原始图片长宽在现定长宽范围以内
					changeToWideth = (int) img_ori_W;
					changeToHeight = (int) img_ori_H;
				}
			} else {// 反之
				if (img_ori_H > height) {
					changeToHeight = (int) height;
					changeToWideth = (int) ((img_ori_W * height) / img_ori_H);
				} else {
					changeToWideth = (int) img_ori_W;
					changeToHeight = (int) img_ori_H;
				}
			}
		}
		srcImage = imageZoomOut(srcImage, changeToWideth, changeToHeight);
		ImageIO.write(srcImage, "JPEG", saveFile);
		map.put("width", changeToWideth);
		map.put("height", changeToHeight);
		return map;
	}
	
	public Map saveImage(BufferedImage _img, String savePath,double width,String formatname) throws Exception {
		Map<String, Integer> map = new HashMap<String, Integer>();
		BufferedImage srcImage = _img; // 获得图片对象
		File saveFile = new File(savePath);
		double img_ori_W = srcImage.getWidth(null);
		double img_ori_H = srcImage.getHeight(null);
		int changeToWideth = 0;
		int changeToHeight = 0;
		if (img_ori_W > 0 && img_ori_H > 0) {
			// flag=true;
			// 原始图片和现定图片长宽形状比较是否宽长于高 或者 比例相等 决定是否按宽来缩放高
			if (img_ori_W > width) {// 原始图片宽是否长于现定图片的宽
				changeToWideth = (int) width;// 设置结果图片的宽为限定宽
				changeToHeight = (int) ((img_ori_H * width) / img_ori_W);// (原始图高
																			// *
																			// 现定图宽)/原始图宽
																			// =
																			// 结果图片的宽

			} else {// 原始图片长宽在现定长宽范围以内
				changeToWideth = (int) img_ori_W;
				changeToHeight = (int) img_ori_H;
			}
		}
		srcImage = imageZoomOut(srcImage, changeToWideth, changeToHeight);
		ImageIO.write(srcImage, formatname.toUpperCase(), saveFile);
		map.put("width", changeToWideth);
		map.put("height", changeToHeight);
		return map;
	}

	public Map saveImage(BufferedImage _img, String savePath,double width) throws Exception {
		return saveImage(_img,savePath,width,"JPEG");
		
	}
	
	public Map saveSmallImage(BufferedImage _img, String savePath,double width,double height,String formatname) throws Exception {
		Map<String, Integer> map = new HashMap<String, Integer>();
		BufferedImage srcImage = _img; // 获得图片对象
		File saveFile = new File(savePath);
		double img_ori_W = srcImage.getWidth(null);
		double img_ori_H = srcImage.getHeight(null);
		int changeToWideth = 0;
		int changeToHeight = 0;
		if (img_ori_W > 0 && img_ori_H > 0) {
			
			if (img_ori_W / img_ori_H >= width / height) {
				if (img_ori_W > width) {// 原始图片宽是否长于现定图片的宽
					changeToWideth = (int) width;// 设置结果图片的宽为限定宽
					changeToHeight = (int) ((img_ori_H * width) / img_ori_W);// (原始图高
																				// *
																				// 现定图宽)/原始图宽
																				// =
																				// 结果图片的宽

				} else {// 原始图片长宽在现定长宽范围以内
					changeToWideth = (int) img_ori_W;
					changeToHeight = (int) img_ori_H;
				}
			} else {// 反之
				if (img_ori_H > height) {
					changeToHeight = (int) height;
					changeToWideth = (int) ((img_ori_W * height) / img_ori_H);
				} else {
					changeToWideth = (int) img_ori_W;
					changeToHeight = (int) img_ori_H;
				}
			}
		}
		srcImage = imageZoomOut(srcImage, changeToWideth, changeToHeight);
		ImageIO.write(srcImage, formatname, saveFile);
		map.put("width", changeToWideth);
		map.put("height", changeToHeight);
		return map;
	}
	
	/**
	 * Start: Use Lanczos filter to replace the original algorithm for image
	 * scaling. Lanczos improves quality of the scaled image modify by :blade
	 */
	public BufferedImage imageZoomOut(BufferedImage srcBufferImage, int w, int h) {
		width = srcBufferImage.getWidth();
		height = srcBufferImage.getHeight();
		scaleWidth = w;

		if (DetermineResultSize(w, h) == 1) {
			return srcBufferImage;
		}
		CalContrib();
		BufferedImage pbOut = HorizontalFiltering(srcBufferImage, w);
		BufferedImage pbFinalOut = VerticalFiltering(pbOut, h);
		return pbFinalOut;
	}

	/**
	 * 决定图像尺寸
	 */
	private int DetermineResultSize(int w, int h) {
		double scaleH, scaleV;
		scaleH = (double) w / (double) width;
		scaleV = (double) h / (double) height;
		// 需要判断一下scaleH，scaleV，不做放大操作
		if (scaleH >= 1.0 && scaleV >= 1.0) {
			return 1;
		}
		return 0;

	} // end of DetermineResultSize()

	private double Lanczos(int i, int inWidth, int outWidth, double Support) {
		double x;

		x = (double) i * (double) outWidth / (double) inWidth;

		return Math.sin(x * PI) / (x * PI) * Math.sin(x * PI / Support)
				/ (x * PI / Support);

	}

	private void CalContrib() {
		nHalfDots = (int) ((double) width * support / (double) scaleWidth);
		nDots = nHalfDots * 2 + 1;
		try {
			contrib = new double[nDots];
			normContrib = new double[nDots];
			tmpContrib = new double[nDots];
		} catch (Exception e) {
			System.out.println("init   contrib,normContrib,tmpContrib" + e);
		}

		int center = nHalfDots;
		contrib[center] = 1.0;

		double weight = 0.0;
		int i = 0;
		for (i = 1; i <= center; i++) {
			contrib[center + i] = Lanczos(i, width, scaleWidth, support);
			weight += contrib[center + i];
		}

		for (i = center - 1; i >= 0; i--) {
			contrib[i] = contrib[center * 2 - i];
		}

		weight = weight * 2 + 1.0;

		for (i = 0; i <= center; i++) {
			normContrib[i] = contrib[i] / weight;
		}

		for (i = center + 1; i < nDots; i++) {
			normContrib[i] = normContrib[center * 2 - i];
		}
	} // end of CalContrib()

	// 处理边缘
	private void CalTempContrib(int start, int stop) {
		double weight = 0;

		int i = 0;
		for (i = start; i <= stop; i++) {
			weight += contrib[i];
		}

		for (i = start; i <= stop; i++) {
			tmpContrib[i] = contrib[i] / weight;
		}

	} // end of CalTempContrib()

	private int GetRedValue(int rgbValue) {
		int temp = rgbValue & 0x00ff0000;
		return temp >> 16;
	}

	private int GetGreenValue(int rgbValue) {
		int temp = rgbValue & 0x0000ff00;
		return temp >> 8;
	}

	private int GetBlueValue(int rgbValue) {
		return rgbValue & 0x000000ff;
	}

	private int ComRGB(int redValue, int greenValue, int blueValue) {

		return (redValue << 16) + (greenValue << 8) + blueValue;
	}

	// 行水平滤波
	private int HorizontalFilter(BufferedImage bufImg, int startX, int stopX,
			int start, int stop, int y, double[] pContrib) {
		double valueRed = 0.0;
		double valueGreen = 0.0;
		double valueBlue = 0.0;
		int valueRGB = 0;
		int i, j;

		for (i = startX, j = start; i <= stopX; i++, j++) {
			valueRGB = bufImg.getRGB(i, y);

			valueRed += GetRedValue(valueRGB) * pContrib[j];
			valueGreen += GetGreenValue(valueRGB) * pContrib[j];
			valueBlue += GetBlueValue(valueRGB) * pContrib[j];
		}

		valueRGB = ComRGB(Clip((int) valueRed), Clip((int) valueGreen),
				Clip((int) valueBlue));
		return valueRGB;

	} // end of HorizontalFilter()

	// 图片水平滤波
	private BufferedImage HorizontalFiltering(BufferedImage bufImage, int iOutW) {
		int dwInW = bufImage.getWidth();
		int dwInH = bufImage.getHeight();
		int value = 0;
		BufferedImage pbOut = new BufferedImage(iOutW, dwInH,
				BufferedImage.TYPE_INT_RGB);

		for (int x = 0; x < iOutW; x++) {

			int startX;
			int start;
			int X = (int) (((double) x) * ((double) dwInW) / ((double) iOutW) + 0.5);
			int y = 0;

			startX = X - nHalfDots;
			if (startX < 0) {
				startX = 0;
				start = nHalfDots - X;
			} else {
				start = 0;
			}

			int stop;
			int stopX = X + nHalfDots;
			if (stopX > (dwInW - 1)) {
				stopX = dwInW - 1;
				stop = nHalfDots + (dwInW - 1 - X);
			} else {
				stop = nHalfDots * 2;
			}

			if (start > 0 || stop < nDots - 1) {
				CalTempContrib(start, stop);
				for (y = 0; y < dwInH; y++) {
					value = HorizontalFilter(bufImage, startX, stopX, start,
							stop, y, tmpContrib);
					pbOut.setRGB(x, y, value);
				}
			} else {
				for (y = 0; y < dwInH; y++) {
					value = HorizontalFilter(bufImage, startX, stopX, start,
							stop, y, normContrib);
					pbOut.setRGB(x, y, value);
				}
			}
		}

		return pbOut;

	} // end of HorizontalFiltering()

	private int VerticalFilter(BufferedImage pbInImage, int startY, int stopY,
			int start, int stop, int x, double[] pContrib) {
		double valueRed = 0.0;
		double valueGreen = 0.0;
		double valueBlue = 0.0;
		int valueRGB = 0;
		int i, j;

		for (i = startY, j = start; i <= stopY; i++, j++) {
			valueRGB = pbInImage.getRGB(x, i);

			valueRed += GetRedValue(valueRGB) * pContrib[j];
			valueGreen += GetGreenValue(valueRGB) * pContrib[j];
			valueBlue += GetBlueValue(valueRGB) * pContrib[j];
			// System.out.println(valueRed+"->"+Clip((int)valueRed)+"<-");
			//     
			// System.out.println(valueGreen+"->"+Clip((int)valueGreen)+"<-");
			// System.out.println(valueBlue+"->"+Clip((int)valueBlue)+"<-"+"-->");
		}

		valueRGB = ComRGB(Clip((int) valueRed), Clip((int) valueGreen),
				Clip((int) valueBlue));
		// System.out.println(valueRGB);
		return valueRGB;

	} // end of VerticalFilter()

	private BufferedImage VerticalFiltering(BufferedImage pbImage, int iOutH) {
		int iW = pbImage.getWidth();
		int iH = pbImage.getHeight();
		int value = 0;
		BufferedImage pbOut = new BufferedImage(iW, iOutH,
				BufferedImage.TYPE_INT_RGB);

		for (int y = 0; y < iOutH; y++) {

			int startY;
			int start;
			int Y = (int) (((double) y) * ((double) iH) / ((double) iOutH) + 0.5);

			startY = Y - nHalfDots;
			if (startY < 0) {
				startY = 0;
				start = nHalfDots - Y;
			} else {
				start = 0;
			}

			int stop;
			int stopY = Y + nHalfDots;
			if (stopY > (int) (iH - 1)) {
				stopY = iH - 1;
				stop = nHalfDots + (iH - 1 - Y);
			} else {
				stop = nHalfDots * 2;
			}

			if (start > 0 || stop < nDots - 1) {
				CalTempContrib(start, stop);
				for (int x = 0; x < iW; x++) {
					value = VerticalFilter(pbImage, startY, stopY, start, stop,
							x, tmpContrib);
					pbOut.setRGB(x, y, value);
				}
			} else {
				for (int x = 0; x < iW; x++) {
					value = VerticalFilter(pbImage, startY, stopY, start, stop,
							x, normContrib);
					pbOut.setRGB(x, y, value);
				}
			}

		}

		return pbOut;

	} // end of VerticalFiltering()

	int Clip(int x) {
		if (x < 0)
			return 0;
		if (x > 255)
			return 255;
		return x;
	}

	/**
	 * End: Use Lanczos filter to replace the original algorithm for image
	 * scaling. Lanczos improves quality of the scaled image modify by :blade
	 **/

	// This method returns a buffered image with the contents of an image

	public BufferedImage toBufferedImage(Image image) {

		if (image instanceof BufferedImage) {

			return (BufferedImage) image;

		}

		// This code ensures that all the pixels in the image are loaded

		image = new ImageIcon(image).getImage();

		// Determine if the image has transparent pixels; for this method's

		// implementation, see Determining If an Image Has Transparent Pixels

		boolean hasAlpha = hasAlpha(image);

		// Create a buffered image with a format that's compatible with the
		// screen

		BufferedImage bimage = null;

		GraphicsEnvironment ge = GraphicsEnvironment

		.getLocalGraphicsEnvironment();

		try {

			// Determine the type of transparency of the new buffered image

			int transparency = Transparency.OPAQUE;

			if (hasAlpha) {

				transparency = Transparency.BITMASK;

			}

			// Create the buffered image

			GraphicsDevice gs = ge.getDefaultScreenDevice();

			GraphicsConfiguration gc = gs.getDefaultConfiguration();

			bimage = gc.createCompatibleImage(image.getWidth(null), image

			.getHeight(null), transparency);

		} catch (HeadlessException e) {

			// The system does not have a screen

			return null;

		}

		if (bimage == null) {

			// Create a buffered image using the default color model

			int type = BufferedImage.TYPE_INT_RGB;

			if (hasAlpha) {

				type = BufferedImage.TYPE_INT_ARGB;

			}

			bimage = new BufferedImage(image.getWidth(null), image

			.getHeight(null), type);

		}

		// Copy image to buffered image

		Graphics g = bimage.createGraphics();

		// Paint the image onto the buffered image

		g.drawImage(image, 0, 0, null);

		g.dispose();

		return bimage;

	}

	// This method returns true if the specified image has transparent pixels

	public boolean hasAlpha(Image image) {

		// If buffered image, the color model is readily available

		if (image instanceof BufferedImage) {

			BufferedImage bimage = (BufferedImage) image;

			return bimage.getColorModel().hasAlpha();

		}

		// Use a pixel grabber to retrieve the image's color model;

		// grabbing a single pixel is usually sufficient

		PixelGrabber pg = new PixelGrabber(image, 0, 0, 1, 1, false);

		try {

			pg.grabPixels();

		} catch (InterruptedException e) {

		}

		// Get the image's color model

		ColorModel cm = pg.getColorModel();

		return cm.hasAlpha();

	}
}
